home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 November: Tool Chest / Dev.CD Nov 96 TC / Dev.CD Nov 96 TC.toast / Tool Chest / Interapplication Communication / Server Remote Control 1.1 / Server Controller.p < prev    next >
Encoding:
Text File  |  1995-06-20  |  30.3 KB  |  1,011 lines  |  [TEXT/MWPS]

  1. {$N-}
  2.  
  3. { Server Controller code }
  4. { © Copyright 1991-1995 Jim Luther, All rights reserved. }
  5.  
  6. { Modification History: }
  7. { 28 Dec 91    JML        V1.0d1    First working version with System 7 File Sharing }
  8. { 30 Dec 91    JML        V1.0d1    Added check for Gestalt and ServerDispatch traps to }
  9. {                            InitializeApp and added kExitNoServerDispatch error string }
  10. {                            resource. }
  11. { 30 Dec 91    JML        V1.0d1    Added gServerKind and gASFileServerFSSpec globals and }
  12. {                            GetServerKind and FindASFileServerApp functions. }
  13. { 30 Dec 91    JML        V1.0d1    Changed RemoteSCPBRec structure (added scSetup field) }
  14. { 30 Dec 91    JML        V1.0d1    Hacked up PPCProcessReads to use SCSleepServer and SCWakeServer }
  15. {                            when the server is AppleShare 3.0. }
  16. { 31 Dec 91    JML        V1.0d1    PPCProcessReads now launches the AppleShare File Server }
  17. {                            application if the SCStartServer command is sent. }
  18. { 31 Dec 91    JML        V1.0d2    Works with AppleShare 3.0. }
  19. {  2 Jan 92    JML        V1.0d2    Got my creator assignments, so I added a BNDL, ICN#, etc and }
  20. {                            changed a few things to use the new creator types and the }
  21. {                            version resources. }
  22. { 10 Mar 93    JML        V1.0    Ahhh, just release the dang thing. It works fine. }
  23. { 21 May 95    JML        V1.1b1    Make it compile with Metrowerks Pascal CW6 and }
  24. {                            Universal Interfaces (what a hassle!). }
  25. { 21 May 95    JML        V1.1b1    Change kMaxSleep from MaxLongInt to 600 to work around }
  26. {                            old WakeUpProcess bug. }
  27. { 22 May 95    JML        V1.1b1    Set gPPCWriteInProgress to false everytime in WriteCompProc to }
  28. {                            fix hang condition. }
  29.  
  30. {$IFC UNDEFINED THINK_Pascal}
  31. {$ELSEC}
  32. {$I-}
  33. {$ENDC}
  34.  
  35. PROGRAM ServerControl;
  36.  
  37.     USES
  38.         AppleTalk, Processes, PPCToolBox, EPPC, Notification, AppleEvents, Script, Finder, Folders, Traps, 
  39. {$IFC UNDEFINED THINK_Pascal}
  40.         ToolUtils, GestaltEqu, Resources, SegLoad, 
  41. {$ENDC}
  42.         ServerControlIntf;
  43.  
  44.     CONST
  45.         kPortName = 'Server Controller';    { should use a resource string }
  46.  
  47.         kControllerCreator = 'ASsc';    { used for our PPC port's portCreator and nbpType }
  48.         kRemoteCreator = 'ASrc';        { used for remote PPC port's portCreator }
  49.  
  50.         kMaxSleep = 600;            { sleep for around 10 seconds when not connected }
  51.         kConnectedSleep = 60;        { how often to poll server when connected }
  52.  
  53.         { PPCReject rejectInfo codes }
  54.         kRemoteIsNotOwner = 1;
  55.         kRemoteAppUnknown = -1;
  56.  
  57.         kExitErrorStrings = 500;
  58.         kNumExitErrors = 12;    { IMPORTANT -- Must match number of exit errors }
  59.         { ExitToShell errors }
  60.         kExitNoSystem7 = 1;
  61.         kExitNoAppleEvts = 2;
  62.         kExitAEHandlerNotInstalled = 3;
  63.         kExitNoOwnerName = 4;
  64.         kExitNoPPC = 5;
  65.         kExitPPCInitFailed = 6;
  66.         kExitAppleTalkDisabled = 7;
  67.         kExitProgramLinkingDisabled = 8;
  68.         kExitPPCOpenFailed = 9;
  69.         kExitPPCInformFailed = 10;
  70.         kExitNoServerDispatch = 11;
  71.         kExitCantFindASFSApp = 12;
  72.         kCantIncreaseStack = 13;
  73.  
  74.         {ServerControl constants}
  75.         kSIMaxLogins = 200;
  76.  
  77.         kVerNumType = $01008000;        { NumVersion.version used for the remote and our }
  78.                                     { PPC ports' portType. In this case version 1.0.0 final. }
  79.  
  80.  
  81.     TYPE
  82.         RemoteSCPBRec = RECORD
  83.                 scPB: SCParamBlockRec;
  84.                 scMessageOrName: Str255;
  85.                 scDiscArray: ARRAY[1..kSIMaxLogins] OF LongInt;
  86.                 scSetup: SetupInfoRec;
  87.             END;
  88.  
  89.         PPCIOBuffer = RemoteSCPBRec;
  90.  
  91.     VAR
  92.         gQuit: Boolean;
  93.         gSleep: LongInt;
  94.         gTicks: LongInt;
  95.  
  96.         gServerType: Integer;    { 0 = System 7 File Sharing; 1 = AppleShare 3.0 }
  97.         gASFileServerFSSpec: FSSpec;    { FSSpec to the AppleShare File Server app if }
  98.                                         { gServerType = AppleShare 3.0 }
  99.         gOwnerName: Str255;
  100.         gOurPSN: ProcessSerialNumber;
  101.  
  102.         gPPCPortOpen: Boolean;            { TRUE when the PPC port has been opened }
  103.         gPPCPortRefNum: PPCPortRefNum;    { The PPC port reference number }
  104.         gPPCSessRefNum: PPCSessRefNum;  { The PPC session reference number }
  105.  
  106.         gPPCGeneralRec: PPCParamBlockRec;
  107.         gPPCSessPortName: PPCPortRec;
  108.         gPPCSessLocationName: LocationNameRec;
  109.         gPPCSessUserName: Str32;
  110.         gPPCReadBuffer: PPCIOBuffer;
  111.         gPPCDataRead: Boolean;
  112.  
  113.         gRemoteSCpb: RemoteSCPBRec;
  114.         gPollSCpb: SCParamBlockRec;
  115.  
  116.         gPPCWriteRec: PPCParamBlockRec;    { used for PPCWrite calls }
  117.         gPPCWriteInProgress: Boolean;
  118.  
  119.         gNotificationMgrPresent: Boolean;
  120.         gNMRec: NMRec;
  121.         gNMStrs: ARRAY[1..kNumExitErrors] OF Str255;
  122.  
  123.  
  124.     { needed forward declarations }
  125.  
  126.     PROCEDURE ReadCompProc (pb: PPCParamBlockPtr);
  127.     FORWARD;
  128.  
  129.     FUNCTION StartPPCInform (pb: PPCParamBlockPtr): OSErr;
  130.     FORWARD;
  131.  
  132.  
  133. {$S Main}
  134.     PROCEDURE NotifyResponseProc (nmReqPtr: NMRecPtr);
  135.         VAR
  136.             oldA5: LongInt;
  137.             err: OSErr;
  138.     BEGIN
  139.         oldA5 := SetA5(nmReqPtr^.nmRefCon);
  140.         gQuit := TRUE;
  141.         err := WakeUpProcess(gOurPSN);
  142.         err := NMRemove(nmReqPtr);
  143.         oldA5 := SetA5(oldA5);
  144.     END;
  145.  
  146.  
  147. {$S Main}
  148.     PROCEDURE NotifyAndExit (errCode: Integer);
  149.     BEGIN
  150.         WITH gNMRec DO
  151.             BEGIN
  152.                 qType := ORD(nmType);
  153.                 nmMark := 0;
  154.                 nmIcon := NIL;
  155.                 nmSound := Handle(-1);
  156.                 nmStr := @gNMStrs[errCode];
  157.                 nmResp := @NotifyResponseProc;
  158.                 nmRefCon := SetCurrentA5;
  159.             END;
  160.         IF (NMInstall(@gNMRec) <> noErr) THEN
  161.             BEGIN
  162.                 gSleep := 10;
  163.                 gQuit := TRUE;
  164.             END;
  165.     END;
  166.  
  167. {-------------------------------------------------------------------------------}
  168. {|    IMPORTANT NOTE                                                                }
  169. {|    From here down to the next comment that looks like this (i.e. starts with    }
  170. {|    an IMPORTANT NOTE title) all the routines defined are completion routines    }
  171. {|    the main trick is that they almost always call some other PPC Toolbox call    }
  172. {|  or somehow tell the main application that it's time to process some data    }
  173. {-------------------------------------------------------------------------------}
  174.  
  175. {------------------------------------------------------------------------------}
  176. {$S Main}
  177.  
  178.     PROCEDURE EndCompProc (pb: PPCParamBlockPtr);
  179.     { This procedure gets called when the asynchronous PPCEnd call completes. }
  180.  
  181.         VAR
  182.             err: OSErr;    { used to catch the PPC function results. }
  183.  
  184.     BEGIN
  185.         IF gPPCPortOpen THEN
  186.             BEGIN
  187.                 gSleep := kMaxSleep; { go back into sleepy mode while no session is active }
  188.                 { use the parameter block to start a new session }
  189.                 err := StartPPCInform(pb);
  190.                 IF err <> noErr THEN
  191.                     BEGIN
  192.                         { Should signal the application that "something really bad" has}
  193.                         { happened and enqueue the parameter block so it doesn't}
  194.                         { get lost.}
  195.                     END;
  196.             END;
  197.         { ELSE port isn't open; things are shutting down so do nothing }
  198.     END;
  199.  
  200. {------------------------------------------------------------------------------}
  201. {$S Main}
  202.  
  203.     PROCEDURE WriteCompProc (pb: PPCParamBlockPtr);
  204.     { This procedure gets called when the asynchronous PPCWrite call completes.}
  205.     { If no errors are detected, then it puts the parameter block in the }
  206.     { gWpbQueue where the PollTheServer procedure will find it next time it }
  207.     { needs to send data to the remote. }
  208.     { If an error is detected, then PPCEnd is called asynchronously to close}
  209.     { the session. }
  210.  
  211.         VAR
  212.             err: OSErr;    { used to catch the PPC function results. }
  213.  
  214.     BEGIN
  215.         gPPCWriteInProgress := FALSE;
  216.         IF PPCWritePBPtr(pb)^.ioResult <> noErr THEN
  217.             BEGIN
  218.             { if we get an error, then we call PPCEnd to close up cleanly}
  219.                 PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc;
  220.                 err := PPCEndAsync(PPCEndPBPtr(pb));
  221.             END;
  222.     END;
  223.  
  224. {------------------------------------------------------------------------------}
  225. {$S Main}
  226.  
  227.     PROCEDURE ReadCompProc (pb: PPCParamBlockPtr);
  228.     { This procedure gets called when the asynchronous PPCRead call completes.}
  229.     { If no errors are detected, then it puts the parameter block in the }
  230.     { gRpbQueue where the PPCProcessReads procedure will find it and process }
  231.     { the data read.  PPCProcessReads will make another PPCRead call. }
  232.     { If an error is detected, then PPCEnd is called asynchronously to close}
  233.     { the session. }
  234.  
  235.         VAR
  236.             err: OSErr;    { used to catch the PPC function results. }
  237.  
  238.     BEGIN
  239.         IF PPCReadPBPtr(pb)^.ioResult = noErr THEN
  240.             BEGIN
  241.                 gPPCDataRead := TRUE;
  242.                 err := WakeUpProcess(gOurPSN);
  243.             END
  244.         ELSE
  245.             BEGIN
  246.                 { if we get an error, then we call PPCEnd to close up cleanly}
  247.                 PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc;
  248.                 err := PPCEndAsync(PPCEndPBPtr(pb));
  249.             END;
  250.     END;
  251.  
  252. {------------------------------------------------------------------------------}
  253. {$S Main}
  254.  
  255.     PROCEDURE RejectCompProc (pb: PPCParamBlockPtr);
  256.     { This procedure gets called when the asynchronous PPCReject call completes. }
  257.  
  258.         VAR
  259.             err: OSErr;    { used to catch the PPC function results. }
  260.  
  261.     BEGIN
  262.         IF gPPCPortOpen THEN { if port isn't open, then things are shutting down so do nothing }
  263.             BEGIN
  264.                 { use the parameter block to start a new session }
  265.                 err := StartPPCInform(pb);
  266.                 IF err <> noErr THEN
  267.                     BEGIN
  268.                         { Should signal the application that "something really bad" }
  269.                         { has happened and enqueue the parameter block so it }
  270.                         { doesn't get lost.}
  271.                     END;
  272.             END;
  273.     END;
  274.  
  275. {------------------------------------------------------------------------------}
  276. {$S Main}
  277.  
  278.     PROCEDURE AcceptCompProc (pb: PPCParamBlockPtr);
  279.     { This procedure gets called when the asynchronous PPCAccept call completes.}
  280.     {  If no errors are detected, then it makes an asynchronous PPCRead call.}
  281.     {  If an error is detected, then it makes an asynchronous PPCEnd call to close}
  282.     {  the session. }
  283.  
  284.         VAR
  285.             err: OSErr;    { used to catch the PPC function results. }
  286.  
  287.     BEGIN
  288.         IF PPCAcceptPBPtr(pb)^.ioResult = noErr THEN
  289.             BEGIN
  290.                 gPPCSessRefNum := PPCAcceptPBPtr(pb)^.sessRefNum;
  291.                 gSleep := kConnectedSleep;
  292.  
  293.                 { start the first PPCRead }
  294.                 WITH PPCReadPBPtr(pb)^ DO
  295.                     BEGIN
  296.                         ioCompletion := @ReadCompProc;
  297.                         { We're reusing the same parameter block, so the sessRefNum}
  298.                         { is already filled in for us. }
  299.  
  300.                         bufferLength := sizeof(PPCIOBuffer);
  301.                         bufferPtr := @gPPCReadBuffer;
  302.                     END;
  303.                 gPPCDataRead := FALSE;
  304.                 err := PPCReadAsync(PPCReadPBPtr(pb));        { asynchronously }
  305.                 err := WakeUpProcess(gOurPSN);
  306.             END
  307.         ELSE
  308.             BEGIN
  309.                 { if we get an error, then we call PPCEnd to close up cleanly}
  310.                 PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc;
  311.                 err := PPCEndAsync(PPCEndPBPtr(pb));
  312.             END;
  313.     END;
  314.  
  315. {------------------------------------------------------------------------------}
  316. {$S Main}
  317.  
  318.     PROCEDURE InformCompProc (pb: PPCParamBlockPtr);
  319.     { This procedure gets called when the asynchronous PPCInform call completes.}
  320.     {  If no errors are detected, then it decides if the session request}
  321.     {  should be accepted of rejected. If the session is accepted, then it makes an}
  322.     {  asynchronous PPCAccept call.    If the session is rejected, then it makes an}
  323.     {  asynchronous PPCReject call (and passes PPCReject the rejectInfo).  If an}
  324.     {  error is detected, then it makes an asynchronous PPCEnd call to close}
  325.     {  the session. }
  326.  
  327.         VAR
  328.             err: OSErr;    { used to catch the PPC function results. }
  329.  
  330.     BEGIN
  331.         IF PPCInformPBPtr(pb)^.ioResult = noErr THEN
  332.             BEGIN
  333.                 { Make sure the owner is connecting with my remote application }
  334.                 IF (EqualString(gPPCSessUserName, gOwnerName, FALSE, TRUE) OR (PPCInformPBPtr(pb)^.requestType = CHAR(ppcLocalOrigin))) AND (PPCInformPBPtr(pb)^.portName^.portKindSelector = ppcByCreatorAndType) AND (PPCInformPBPtr(pb)^.portName^.portCreator = kRemoteCreator) AND (LongInt(PPCInformPBPtr(pb)^.portName^.portType) = kVerNumType) THEN
  335.                     BEGIN
  336.                         PPCAcceptPBPtr(pb)^.ioCompletion := @AcceptCompProc;
  337.                         { We're reusing the same parameter block, so the}
  338.                         { sessRefNum is already filled in for us. }
  339.                         err := PPCAcceptAsync(PPCAcceptPBPtr(pb)); { asynchronous }
  340.                     END
  341.                 ELSE
  342.                     BEGIN
  343.                         WITH PPCRejectPBPtr(pb)^ DO
  344.                             BEGIN
  345.                                 ioCompletion := @RejectCompProc;
  346.                                 { We're reusing the same parameter block, so the}
  347.                                 { sessRefNum is already filled in for us. }
  348.  
  349.                                 { Set the rejectInfo field }
  350.                                 IF gPPCSessUserName <> gOwnerName THEN
  351.                                     rejectInfo := kRemoteIsNotOwner
  352.                                 ELSE
  353.                                     rejectInfo := kRemoteAppUnknown;
  354.                             END;
  355.                         err := PPCRejectAsync(PPCRejectPBPtr(pb)); { asynchronous }
  356.                     END;
  357.             END
  358.         ELSE
  359.             BEGIN
  360.                 { if we get an error, then we call PPCEnd to close up cleanly}
  361.                 PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc;
  362.                 err := PPCEndAsync(PPCEndPBPtr(pb));
  363.             END;
  364.     END;
  365.  
  366. {------------------------------------------------------------------------------}
  367. {| IMPORTANT NOTE:}
  368. {|        All the procedures above here are executed at interrupt time.  Each}
  369. {|        one of them is used as a completion routine.}
  370. {-------------------------------------------------------------------------------}
  371.  
  372.  
  373. {$S Main}
  374.     FUNCTION StartPPCInform (pb: PPCParamBlockPtr): OSErr;
  375.     { Initializes the session record's fields, and calls PPCInform to receive}
  376.     {  another PPC session request.}
  377.     {  Any errors detected are passed back to the caller. }
  378.         VAR
  379.             err: OSErr;
  380.     BEGIN
  381.         { set up the parameter block for a PPCInform call }
  382.         WITH PPCInformPBPtr(pb)^ DO
  383.             BEGIN
  384.                 ioCompletion := @InformCompProc;
  385.                 portRefNum := gPPCPortRefNum;
  386.                 autoAccept := FALSE;    { we will approve sessions before accepting them }
  387.                 { The portName, locationName, and userName records }
  388.                 { are filled in by PPCInform.  They tell you the }
  389.                 { port name, location name, and user name of the }
  390.                 { destination port that is attempting to start a }
  391.                 { session with us. }
  392.                 portName := @gPPCSessPortName;
  393.                 locationName := @gPPCSessLocationName;
  394.                 userName := @gPPCSessUserName;
  395.             END;
  396.         { execute PPCInform asynchronously and return any errors to caller }
  397.         StartPPCInform := PPCInformAsync(PPCInformPBPtr(pb));
  398.     END;
  399.  
  400.  
  401. {$S Main}
  402.     FUNCTION OpenPPCPort: OSErr;
  403.     { OpenPPCPort opens a PPC port for use by the server sessions.}
  404.     {  It initializes the port name and location name records.}
  405.     {  Then, it calls PPCOpen synchronously to open the port.  If the port was}
  406.     {  sucessfully opened, the gPPCPortOpen is set TRUE, and gPPCPortRefNum is set to}
  407.     {  the port reference number returned by PPCOpen.}
  408.     {  Any errors detected are passed back to PPCStartUp to be returned to}
  409.     {  the application. }
  410.  
  411.         VAR
  412.             thePortRec: PPCPortRec;                { the port name of the port to be opened. }
  413.             theLocationName: LocationNameRec;    { location name of the port to be opened. }
  414.             theOpenPBRec: PPCOpenPBRec;            { used by the PPCOpen call. }
  415.             err: OSErr;    { used to keep track of errors within the function. }
  416.     BEGIN
  417.         { initialize the port name record }
  418.         WITH thePortRec DO
  419.             BEGIN
  420.                 nameScript := GetScriptManagerVariable(smSysScript);    { use Script Manager call to get System Script }
  421.                 name := kPortName;                { This is the name that will show up in the }
  422.                                                 { "Programs" list that the Browser puts up.}
  423.                                                 { It should be a resource string instead of }
  424.                                                 { hard coded (as done here).}
  425.                 portKindSelector := ppcByCreatorAndType;    { port kind by creator/type }
  426.                 portCreator := kControllerCreator;
  427.                 portType := OSType(kVerNumType);
  428.             END;
  429.  
  430.         { initialize the location name record }
  431.         WITH theLocationName DO
  432.             BEGIN
  433.                 locationKindSelector := ppcNBPTypeLocation;
  434.                 nbpType := kControllerCreator;
  435.             END;
  436.  
  437.         { Now, set up Open parameter block record }
  438.         WITH theOpenPBRec DO
  439.             BEGIN
  440.                 ioCompletion := NIL;                { no completion Proc needed (synchronous) }
  441.                 serviceType := CHAR(ppcServiceRealTime);    { 7.0 only supports this type of service }
  442.                 resFlag := 0;                        { must be zero }
  443.                 portName := @thePortRec;            { pointer to port record}
  444.                 locationName := @theLocationName;    { pointer to location name record }
  445.                 networkVisible := TRUE;             { Yes, let other PPC users see us! }
  446.             END;
  447.  
  448.         { execute PPCOpen synchronously and return any errors to caller }
  449.         err := PPCOpen(@theOpenPBRec, FALSE);
  450.         IF err = noErr THEN
  451.             BEGIN
  452.                 gPPCPortOpen := TRUE;                { set the global port open flag }
  453.                 gPPCPortRefNum := theOpenPBRec.portRefNum;    { set the global port reference number }
  454.             END;
  455.         OpenPPCPort := err;
  456.     END;
  457.  
  458.  
  459. {$S Main}
  460.     PROCEDURE PPCShutDown;
  461.     { PPCShutDown first closes the PPC port that was opened by PPCStartUp.}
  462.     {  Closing the port will automatically kill all sessions that use that port.}
  463.     {  After closing the port, PPCShutDown disposes of all session records. }
  464.  
  465.         VAR
  466.             theClosePBRec: PPCClosePBRec;
  467.             err: OSErr;
  468.     BEGIN
  469.         { Close the port.  This will cause all PPC calls associated with this port }
  470.         { to complete. }
  471.         IF gPPCPortOpen THEN    { close the port }
  472.             BEGIN
  473.                 gPPCPortOpen := FALSE;    { tell completion routines we're shutting down }
  474.                                         { so they won't try to restart a session }
  475.                 theClosePBRec.ioCompletion := NIL;
  476.                 theClosePBRec.portRefNum := gPPCPortRefNum;
  477.                 err := PPCClose(@theClosePBRec, FALSE);
  478.             END;
  479.     END;
  480.  
  481.  
  482. {$S Initialize}
  483.     FUNCTION InitPPCStuff: Boolean;
  484.         VAR
  485.             PPCAttributes: LongInt;    {Storage for the response from Gestalt}
  486.             err: OSErr;    {Temporary variable to catch errors}
  487.     BEGIN
  488.         InitPPCStuff := FALSE;
  489.         IF Gestalt(gestaltPPCToolboxAttr, PPCAttributes) <> noErr THEN
  490.             BEGIN
  491.                 NotifyAndExit(kExitNoPPC); { ••• Bail out now ••• }
  492.                 Exit(InitPPCStuff);
  493.             END;
  494.  
  495.         { ELSE PPC Toolbox is present }
  496.  
  497.         { Does PPC need initialization? }
  498.         IF BAND(PPCAttributes, gestaltPPCSupportsRealTime) = 0 THEN
  499.             BEGIN    { PPC Toolbox needs initialization }
  500.                 { initialize the PPC Toolbox and set function result }
  501.                 IF PPCInit = noErr THEN
  502.                     { get the post-init attributes for the PPC Toolbox }
  503.                     err := Gestalt(gestaltPPCToolboxAttr, PPCAttributes)
  504.                 ELSE    { PPC can't be inited }
  505.                     BEGIN
  506.                         NotifyAndExit(kExitPPCInitFailed); { ••• Bail out now ••• }
  507.                         Exit(InitPPCStuff);
  508.                     END;
  509.             END;
  510.  
  511.         { Make sure ports can be opened to the outside world }
  512.         IF BAND(PPCAttributes, gestaltPPCSupportsOutGoing) = 0 THEN
  513.             { It's likely that AppleTalk is disabled, so you    }
  514.             { may want to tell the user to activate AppleTalk    }
  515.             { from the Chooser.    }
  516.             BEGIN
  517.                 NotifyAndExit(kExitAppleTalkDisabled); { ••• Bail out now ••• }
  518.                 Exit(InitPPCStuff);
  519.             END;
  520.  
  521.         { Make sure ports can be opened with location names that the }
  522.         { outside world can see }
  523.         IF BAND(PPCAttributes, gestaltPPCSupportsIncoming) = 0 THEN
  524.             { It's likely that Program Linking is disabled, so you    }
  525.             { may want to tell the user to start Program Linking    }
  526.             { from the Sharing Setup control panel.    }
  527.             BEGIN
  528.                 NotifyAndExit(kExitProgramLinkingDisabled); { ••• Bail out now ••• }
  529.                 Exit(InitPPCStuff);
  530.             END;
  531.  
  532.         IF OpenPPCPort <> noErr THEN
  533.             { couldn't open a PPC port }
  534.             BEGIN
  535.                 NotifyAndExit(kExitPPCOpenFailed); { ••• Bail out now ••• }
  536.                 Exit(InitPPCStuff);
  537.             END;
  538.  
  539.         IF StartPPCInform(@gPPCGeneralRec) <> noErr THEN
  540.             BEGIN
  541.                 PPCShutDown; { close the port }
  542.                 NotifyAndExit(kExitPPCInformFailed); { ••• Bail out now ••• }
  543.                 Exit(InitPPCStuff);
  544.             END;
  545.         InitPPCStuff := TRUE;
  546.     END;
  547.  
  548.  
  549. {$S Main}
  550. { This is the standard Open Application event. }
  551.     FUNCTION AEOpenHandler (messagein: AppleEvent;
  552.                                     reply: AppleEvent;
  553.                                     refIn: LongInt): OSErr;
  554.     BEGIN
  555.     { we of course don't do anything here }
  556.         AEOpenHandler := noErr;
  557.     END;
  558.  
  559.  
  560. {$S Main}
  561.     FUNCTION AEOpenDocHandler (messagein: AppleEvent;
  562.                                     reply: AppleEvent;
  563.                                     refIn: LongInt): OSErr;
  564.     BEGIN
  565.     { we of course don't do anything here }
  566.         AEOpenDocHandler := errAEEventNotHandled;    { we have no docs, so no odoc events should come to us }
  567.     END;
  568.  
  569.  
  570. {$S Main}
  571.     FUNCTION AEPrintHandler (messagein: AppleEvent;
  572.                                     reply: AppleEvent;
  573.                                     refIn: LongInt): OSErr;
  574.     BEGIN
  575.     { we of course don't do anything here }
  576.         AEPrintHandler := errAEEventNotHandled;    { we have no docs, so no pdoc events should come to us }
  577.     END;
  578.  
  579.  
  580. {$S Main}
  581. { Standard Quit event handler, to handle a Quit event from the Finder, for example.    }
  582. { ••••• DO NOT CALL EXITTOSHELL HERE ••••• or you will never have a happy life.    }
  583.     FUNCTION AEQuitHandler (messagein: AppleEvent;
  584.                                     reply: AppleEvent;
  585.                                     refIn: LongInt): OSErr;
  586.     BEGIN
  587.     { prepQuit sets the Stop flag for us.  It does _NOT_ quit, you }
  588.     { should NEVER quit from an AppleEvent handler.  Calling }
  589.     { ExitToShell here would blow things up }
  590.         gQuit := TRUE;
  591.         AEQuitHandler := noErr;
  592.     END;
  593.  
  594.  
  595. {$S Main}
  596. { I'm not doing any error handling here because there's }
  597. { not a lot I can do; just pass the errors back. }
  598.     PROCEDURE DoHighLevel (AERecord: EventRecord);
  599.         VAR
  600.             err: OSErr;
  601.     BEGIN
  602.         err := AEProcessAppleEvent(AERecord);
  603.     END;
  604.  
  605.  
  606. {$S Main}
  607.     PROCEDURE PPCProcessReads;
  608. {••••• My initial design was flawed so I'm gonna have to hack this up to get it }
  609. {••••• to work with AS 3.0 and System 7.  1.0 d3 will fix it. }
  610.         VAR
  611.             pbPtr: PPCParamBlockPtr;
  612.             err: OSErr;
  613.             launchPB: LaunchParamBlockRec;
  614.     BEGIN
  615.         IF gPPCDataRead THEN    { process the data read }
  616.             BEGIN
  617.                 { Make a server control call based on the data just read }
  618.  
  619.                 { move the parameter block data into the local SCParamBlockRec }
  620.                 BlockMove(gPPCGeneralRec.readParam.bufferPtr, @gRemoteSCpb, sizeof(RemoteSCPBRec));
  621.                 { set up any parameter block pointers and call ServerDispatch }
  622.                 CASE gRemoteSCpb.scPB.disconnectPB.scCode OF
  623.                     SCStartServer: 
  624.                         BEGIN
  625.                             CASE gServerType OF
  626.                                 0: { System 7 File Sharing }
  627.                                     err := SyncServerDispatch(@gRemoteSCpb.scPB);
  628.                                 1: 
  629.                                     BEGIN
  630.                                     { This call won't happen unless the AppleShare File }
  631.                                     { Server application isn't running, so we need to }
  632.                                     { launch it. }
  633.                                         WITH launchPB DO
  634.                                             BEGIN
  635.                                                 launchBlockID := extendedBlock;
  636.                                                 launchEPBLength := extendedBlockLen;
  637.                                                 launchFileFlags := 0;
  638.                                                 launchControlFlags := launchContinue + launchNoFileFlags;
  639.                                                 launchAppSpec := @gASFileServerFSSpec;
  640.                                                 launchAppParameters := NIL;
  641.                                             END;
  642.                                         err := LaunchApplication(@launchPB);
  643.                                     END;
  644.                             END;
  645.                         END;
  646.                     SCShutDown: 
  647.                         BEGIN
  648.                             gRemoteSCpb.scPB.disconnectPB.scMessagePtr := @gRemoteSCpb.scMessageOrName;
  649.  
  650.                             {••••• HACK ALERT!!!  Will delete this IF statement at 1.0d3 }
  651.                             IF gServerType = 1 THEN
  652.                                 gRemoteSCpb.scPB.disconnectPB.scCode := SCSleepServer;
  653.  
  654.                             err := SyncServerDispatch(@gRemoteSCpb.scPB);
  655.                         END;
  656.                     SCCancelShutDown: 
  657.                         BEGIN
  658.                             err := SyncServerDispatch(@gRemoteSCpb.scPB);
  659.                         END;
  660.                     SCWakeServer: 
  661.                         BEGIN
  662.                             err := SyncServerDispatch(@gRemoteSCpb.scPB);
  663.                         END;
  664.                 END;
  665.  
  666.                 { OK, the call was made, so fill out the readParam parameter block }
  667.                 { and call PPCRead }
  668.                 pbPtr := @gPPCGeneralRec;
  669.                 WITH PPCReadPBPtr(pbPtr)^ DO
  670.                     BEGIN
  671.                         ioCompletion := @ReadCompProc;
  672.                         bufferLength := sizeof(PPCIOBuffer); { full buffer size again }
  673.                         { We're reusing the same parameter block, so the sessRefNum }
  674.                         { and bufferPtr fields are already filled in for us. }
  675.                         bufferPtr := @gPPCReadBuffer;
  676.                     END;
  677.                 gPPCDataRead := FALSE;
  678.                 err := PPCReadAsync(PPCReadPBPtr(pbPtr));     { asynchronously }
  679.             END;
  680.     END;
  681.  
  682.  
  683. {$S Main}
  684.     PROCEDURE PollTheServer;
  685.         VAR
  686.             err: OSErr;
  687.     BEGIN
  688.         IF NOT gPPCWriteInProgress THEN
  689.             BEGIN
  690.                 gPollSCpb.pollServerPB.scCode := SCPollServer;
  691.                 IF SyncServerDispatch(@gPollSCpb) = noErr THEN
  692.                     BEGIN
  693.                         {now, send the gPollSCpb to the remote}
  694.                         WITH gPPCWriteRec.writeParam DO
  695.                             BEGIN
  696.                                 ioCompletion := @WriteCompProc;
  697.                                 sessRefNum := gPPCSessRefNum;
  698.                                 bufferLength := sizeof(SCParamBlockRec);
  699.                                 bufferPtr := @gPollSCpb;
  700.                                 more := FALSE;
  701.                                 { I'm not using userData, blockCreator, or blockType }
  702.                             END;
  703.                         gPPCWriteInProgress := TRUE;
  704.                         err := PPCWriteAsync(PPCWritePBPtr(@gPPCWriteRec));
  705.                     END;
  706.             END;
  707.     END;
  708.  
  709.  
  710. {$S Main}
  711.     PROCEDURE doNullEvt;
  712.         VAR
  713.             ticks: LongInt;
  714.     BEGIN
  715.         ticks := TickCount;
  716.         IF ticks - gTicks >= gSleep THEN
  717.             BEGIN
  718.                 IF gPPCSessRefNum <> 0 THEN
  719.                     BEGIN
  720.                         PPCProcessReads;
  721.                         PollTheServer;
  722.                     END;
  723.                 gTicks := ticks;
  724.             END;
  725.     END;
  726.  
  727.  
  728. {$S Main}
  729.     PROCEDURE DoEventLoop;
  730.         VAR
  731.             evtRecord: EventRecord;
  732.             bob: Boolean;
  733.     BEGIN
  734.         REPEAT
  735.             bob := WaitNextEvent(highLevelEventMask, evtRecord, gSleep, NIL);
  736.             CASE evtRecord.what OF
  737.                 nullEvent: 
  738.                     doNullEvt;
  739.                 kHighLevelEvent: 
  740.                     DoHighLevel(evtRecord);
  741.             END; { CASE evtRecord.what }
  742.         UNTIL gQuit = TRUE;
  743.     END;    { DoEventLoop }
  744.  
  745.  
  746. {$S Initialize}
  747.     FUNCTION InitAEStuff: Boolean;
  748.         VAR
  749.             err: OSErr;
  750.     BEGIN
  751.         { The following series of calls install our AppleEvent Handlers.    }
  752.         { These handlers are added to the application event handler list    }
  753.         { that the AppleEvent manager maintains.  So, whenever an            }
  754.         { AppleEvent happens and we call AEProcessEvent, the AppleEvent        }
  755.         { manager will check our list of handlers and dispatch to the        }
  756.         { the correct handler if there is one.    }
  757.         err := AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, @AEOpenHandler, 0, false);
  758.         IF err = noErr THEN
  759.             err := AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, @AEOpenDocHandler, 0, false);
  760.         IF err = noErr THEN
  761.             err := AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, @AEQuitHandler, 0, false);
  762.         IF err = noErr THEN
  763.             err := AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, @AEPrintHandler, 0, false);
  764.  
  765.         IF err <> noErr THEN
  766.             NotifyAndExit(kExitAEHandlerNotInstalled); { ••• Bail out now ••• }
  767.  
  768.         InitAEStuff := (err = noErr);
  769.     END;
  770.  
  771.  
  772. {$S Initialize}
  773.     FUNCTION NumToolboxTraps: Integer;
  774.     BEGIN
  775.         IF NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) THEN
  776.             NumToolboxTraps := $200
  777.         ELSE
  778.             NumToolboxTraps := $400;
  779.     END;
  780.  
  781.  
  782. {$S Initialize}
  783.     FUNCTION GetTrapType (theTrap: Integer): TrapType;
  784.         CONST
  785.             TrapMask = $0800;
  786.     BEGIN
  787.         IF BAND(theTrap, TrapMask) > 0 THEN
  788.             GetTrapType := ToolTrap
  789.         ELSE
  790.             GetTrapType := OSTrap;
  791.     END;
  792.  
  793.  
  794. {$S Initialize}
  795.     FUNCTION TrapAvailable (theTrap: Integer): Boolean;
  796.         VAR
  797.             tType: TrapType;
  798.     BEGIN
  799.         tType := GetTrapType(theTrap);
  800.         IF tType = ToolTrap THEN
  801.             BEGIN
  802.                 theTrap := BAND(theTrap, $07FF);
  803.                 IF theTrap >= NumToolboxTraps THEN
  804.                     theTrap := _Unimplemented;
  805.             END;
  806.         TrapAvailable := NGetTrapAddress(theTrap, tType) <> NGetTrapAddress(_Unimplemented, ToolTrap)
  807.     END;
  808.  
  809.  
  810. {$S Initialize}
  811.     FUNCTION FindASFileServerApp (VAR theFSSpec: FSSpec): OSErr;
  812.         VAR
  813.             err: OSErr;
  814.             foundVRefNum: Integer;
  815.             foundDirID: LongInt;
  816.             pb: HParamBlockRec;
  817.             spec1: CInfoPBRec;
  818.             spec2: CInfoPBRec;
  819.  
  820.     BEGIN
  821.          { get the vRefNum of the boot disk }
  822.         err := FindFolder(kOnSystemDisk, kSystemFolderType, FALSE, foundVRefNum, foundDirID);
  823.  
  824.         { look for the AppleShare File Server application }
  825.         WITH pb DO
  826.             BEGIN
  827.                 ioCompletion := NIL;
  828.                 ioNamePtr := NIL;
  829.                 ioVRefNum := foundVRefNum;
  830.                 ioMatchPtr := FSSpecArrayPtr(@theFSSpec);
  831.                 ioReqMatchCount := 1;
  832.                 ioSearchBits := fsSBFlFndrInfo; { look only at ioFlFndrInfo}
  833.                 ioSearchInfo1 := @spec1;
  834.                 ioSearchInfo2 := @spec2;
  835.                 ioSearchTime := 0;
  836.                 ioCatPosition.initialize := 0;
  837.                 ioOptBuffer := NIL;
  838.                 ioOptBufSize := 0;
  839.             END;
  840.         WITH spec1.ioFlFndrInfo DO
  841.             BEGIN
  842.                 fdType := 'APPL';        {••••• should be constant }
  843.                 fdCreator := 'hgfd';    {••••• should be constant }
  844.                 fdFlags := 0;
  845.                 fdLocation.v := 0;
  846.                 fdLocation.h := 0;
  847.                 fdFldr := 0;
  848.             END;
  849.         WITH spec2.ioFlFndrInfo DO
  850.             BEGIN
  851.                 fdType := OSType(-1);
  852.                 fdCreator := OSType(-1);
  853.                 fdFlags := 0;
  854.                 fdLocation.v := 0;
  855.                 fdLocation.h := 0;
  856.                 fdFldr := 0;
  857.             END;
  858.  
  859.         err := PBCatSearchSync(@pb);
  860.  
  861.         IF ((err = noErr) OR (err = eofErr)) AND (pb.ioActMatchCount > 0) THEN
  862.             FindASFileServerApp := noErr    { we found it! }
  863.         ELSE
  864.             FindASFileServerApp := fnfErr;    { we didn't find it }
  865.     END;
  866.  
  867. {$S Initialize}
  868.     FUNCTION GetServerType: Integer;
  869.         VAR
  870.             scPB: SCParamBlockRec;
  871.     BEGIN
  872.         scPB.versionPB.scExtNamePtr := NIL;
  873.         scPB.versionPB.scCode := SCServerVersion;
  874.         IF SyncServerDispatch(@scPB) = noErr THEN
  875.             ;{ do nothing }
  876.         GetServerType := scPB.versionPB.scServerType;
  877.     END;
  878.  
  879. {$S Initialize}
  880.     PROCEDURE IncreaseStackSize (extraBytes: Size);
  881.     BEGIN
  882.         SetApplLimit(Ptr(ORD4(GetApplLimit) - extraBytes));
  883.     END;
  884.  
  885.  
  886. {$S Initialize}
  887.     PROCEDURE InitializeApp;
  888.         VAR
  889.             vers: LongInt;
  890.             err: OSErr;
  891.             aLong: LongInt;
  892.             ownerName: StringHandle;
  893.             savedResFile: Integer;
  894.             i: Integer;
  895.             curVersion: VersRecHndl;
  896.     BEGIN
  897.         gQuit := FALSE;
  898.         gSleep := kMaxSleep;    {sleep until we have something to do}
  899.  
  900.         gPPCPortOpen := FALSE;
  901.         gPPCPortRefNum := 0;
  902.         gPPCSessRefNum := 0;
  903.         gPPCDataRead := FALSE;
  904.         gPPCWriteInProgress := FALSE;
  905.  
  906.         InitGraf(@qd.thePort);
  907.  
  908.         err := GetCurrentProcess(gOurPSN);    { so completion routines can wake us up }
  909.  
  910.         IF (ORD4(GetApplLimit) - ORD4(GetZone^.bkLim)) < 2048 THEN
  911.             BEGIN
  912.                 NotifyAndExit(kCantIncreaseStack); { ••• Bail out now ••• }
  913.                 Exit(InitializeApp);
  914.             END;
  915.         IncreaseStackSize(2048); { increase stack by 2K }
  916.  
  917.         MaxApplZone;
  918.  
  919.  
  920.         IF NOT TrapAvailable(_Gestalt) THEN
  921.             BEGIN
  922.                 { If Gestalt isn't available, then we can't even notify the user }
  923.                 { because we can't see if the Notification Manager is available }
  924.                 SysBeep(1);        { so ring the bell }
  925.                 ExitToShell;    { and exit }
  926.             END;
  927.  
  928.         { see if Notification Manager is available to display error messages }
  929.         gNotificationMgrPresent := Gestalt(gestaltNotificationMgrAttr, aLong) = noErr;
  930.  
  931.         FOR i := 1 TO kNumExitErrors DO
  932.             GetIndString(gNMStrs[i], kExitErrorStrings, i);
  933.  
  934.         { Check system version }
  935.         vers := 0;
  936.         err := Gestalt(gestaltSystemVersion, vers);
  937.         IF LoWord(vers) < $0700 THEN
  938.             BEGIN
  939.                 NotifyAndExit(kExitNoSystem7); { ••• Bail out now ••• }
  940.                 Exit(InitializeApp);
  941.             END;
  942.  
  943.         { Check this machine for AppleEvents. }
  944.         { If they are not here, then we exit }
  945.         IF (Gestalt(gestaltAppleEventsAttr, aLong) <> noErr) THEN
  946.             BEGIN
  947.                 NotifyAndExit(kExitNoAppleEvts); { ••• Bail out now ••• }
  948.                 Exit(InitializeApp);
  949.             END;
  950.  
  951.         IF NOT InitAEStuff THEN
  952.             Exit(InitializeApp);
  953.  
  954.         { Make sure ServerDispatch trap is available }
  955.         IF NOT TrapAvailable(ServerDispatch) THEN
  956.             BEGIN
  957.                 NotifyAndExit(kExitNoServerDispatch); { ••• Bail out now ••• }
  958.                 Exit(InitializeApp);
  959.             END;
  960.  
  961.         gServerType := GetServerType;
  962.         IF gServerType = 1 THEN
  963.             { we need to know where the AS File Server application is }
  964.             { in case we need to launch it. }
  965.             IF FindASFileServerApp(gASFileServerFSSpec) <> noErr THEN
  966.                 BEGIN
  967.                     NotifyAndExit(kExitCantFindASFSApp); { ••• Bail out now ••• }
  968.                     Exit(InitializeApp);
  969.                 END;
  970.  
  971.         { Get the Macintosh owner's name from the System file }
  972.         { If we can't get it, then exit because we have to have it }
  973.         { to ensure the owner is the remote user }
  974.         { ••••• Although I get the name here, I should probably hook into the }
  975.         { ••••• AppleTalk transition queue to catch any name changes. }
  976.         savedResFile := CurResFile;
  977.         UseResFile(0);
  978.         ownerName := StringHandle(GetResource('STR ', -16096));
  979.         UseResFile(savedResFile);
  980.         IF ownerName <> NIL THEN
  981.             BEGIN
  982.                 { keep a global copy of the owner name }
  983.                 gOwnerName := ownerName^^;
  984.                 { and release the ownerName resource }
  985.                 ReleaseResource(Handle(ownerName));
  986.             END
  987.         ELSE
  988.             BEGIN
  989.                 NotifyAndExit(kExitNoOwnerName); { ••• Bail out now ••• }
  990.                 Exit(InitializeApp);
  991.             END;
  992.  
  993.         IF NOT InitPPCStuff THEN
  994.             Exit(InitializeApp);
  995.     END;
  996.  
  997.  
  998. {PROCEDURE _DataInit;}
  999. {External;}
  1000. { this is the MPW application initialization code }
  1001.  
  1002.  
  1003. {$S Main}
  1004. BEGIN
  1005.     {UnloadSeg(@_DataInit);}
  1006.     { throw out the setup code }
  1007.     InitializeApp;
  1008.     UnloadSeg(@InitializeApp);    { get rid of my initialization code }
  1009.     DoEventLoop;
  1010.     PPCShutDown;
  1011. END. { Main }